package com.example.graphqlserver;

import graphql.execution.instrumentation.Instrumentation;
import graphql.introspection.Introspection;
import graphql.relay.Connection;
import graphql.relay.DefaultConnection;
import graphql.relay.Edge;
import graphql.relay.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.graphql.GraphQlAutoConfiguration;
import org.springframework.boot.autoconfigure.graphql.GraphQlProperties;
import org.springframework.boot.autoconfigure.graphql.GraphQlSourceBuilderCustomizer;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.log.LogMessage;
import org.springframework.data.domain.Page;
import org.springframework.data.querydsl.binding.QuerydslBinderCustomizer;
import org.springframework.graphql.execution.*;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Configuration(proxyBeanMethods = false)
public class GraphQlConfig {


//    public <T> Connection<T> createConnection(Page<T> page, Class<T> clazz) {
//        List<Edge<T>> edges = page.getContent().stream()
//                .map(node -> new Edge<>(String.valueOf(node.getId()), node))
//                .collect(Collectors.toList());
//
//        PageInfo pageInfo = new PageInfo(
//                page.hasNext(),
//                page.hasPrevious(),
//                page.getContent().get(0).getId().toString(),
//                page.getContent().get(page.getContent().size() - 1).getId().toString()
//        );
//
//        return new DefaultConnection<>(edges, pageInfo);
//    }

    @Bean
    public QuerydslBinderCustomizer querydslBinderCustomizer() {
        return (dsl, builder) -> {
            System.out.println(">>>>>");
              builder.getMetadata();
//            builder.bind("name").to(String.class);
//            builder.bind("email").to(String.class);
//            builder.bind
        };
    }





//    @Bean
    public GraphQlSourceBuilderCustomizer sourceBuilderCustomizer() {
        return (builder) ->{
//            builder.configureGraphQl((graphQlBuilder) -> {
//                graphQlBuilder.executionIdProvider(new ExecutionIdProvider(){
//
//                    @Override
//                    public ExecutionId provide(String query, String operationName, Object context) {
//                        return null;
//                    }
//                });
//            });
//            builder.configureRuntimeWiring((runtimeWiring) -> {
//                runtimeWiring.scalar(ExtendedScalars.Date);
//            });
//            builder.exceptionResolvers();
        };

    }


    private static final Log logger = LogFactory.getLog(GraphQlAutoConfiguration.class);
    @Bean
    @ConditionalOnMissingBean
    public GraphQlSource graphQlSource(ResourcePatternResolver resourcePatternResolver, GraphQlProperties properties,
                                       ObjectProvider<DataFetcherExceptionResolver> exceptionResolvers,
                                       ObjectProvider<SubscriptionExceptionResolver> subscriptionExceptionResolvers,
                                       ObjectProvider<Instrumentation> instrumentations, ObjectProvider<RuntimeWiringConfigurer> wiringConfigurers,
                                       ObjectProvider<GraphQlSourceBuilderCustomizer> sourceCustomizers) {
        String[] schemaLocations = properties.getSchema().getLocations();
        Resource[] schemaResources = resolveSchemaResources(resourcePatternResolver, schemaLocations,
                properties.getSchema().getFileExtensions());
        GraphQlSource.SchemaResourceBuilder builder = GraphQlSource.schemaResourceBuilder()
                .schemaResources(schemaResources)
                .exceptionResolvers(exceptionResolvers.orderedStream().toList())
                .subscriptionExceptionResolvers(subscriptionExceptionResolvers.orderedStream().toList())
                .instrumentation(instrumentations.orderedStream().toList());
        if (properties.getSchema().getInspection().isEnabled()) {
            builder.inspectSchemaMappings(logger::debug);
        }
        if (!properties.getSchema().getIntrospection().isEnabled()) {
            Introspection.enabledJvmWide(true);
        }
        builder.configureTypeDefinitions(new ConnectionTypeDefinitionConfigurer());
        wiringConfigurers.orderedStream().forEach(builder::configureRuntimeWiring);
        sourceCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
        return builder.build();
    }
    private Resource[] resolveSchemaResources(ResourcePatternResolver resolver, String[] locations,
                                              String[] extensions) {
        List<Resource> resources = new ArrayList<>();
        for (String location : locations) {
            for (String extension : extensions) {
                resources.addAll(resolveSchemaResources(resolver, location + "*" + extension));
            }
        }
        return resources.toArray(new Resource[0]);
    }
    private List<Resource> resolveSchemaResources(ResourcePatternResolver resolver, String pattern) {
        try {
            return Arrays.asList(resolver.getResources(pattern));
        }
        catch (IOException ex) {
            logger.debug(LogMessage.format("Could not resolve schema location: '%s'", pattern), ex);
            return Collections.emptyList();
        }
    }

}